package ai;

import java.util.ArrayList;
import hexxagon.Hexxagon;
import java.util.logging.Level;
import java.util.logging.Logger;

public class GameBoard {

    private int currentPlayer;
    private Piece[][] board = new Piece[9][9];
    /**
     * mang luu tru so quan cua moi ben:<p>
     * countPieces[1]: dem so quan cua nguoi<p>
     * countPieces[2]: dem so quan cua may
     */
    public int[] countPieces = new int[3];

    public Piece[][] getBoard() {
        return board;
    }

    public int getCurrentPlayer() {
        return currentPlayer;
    }

    public void setCurrentPlayer(int currentPlayer) {
        this.currentPlayer = currentPlayer;
    }

    public GameBoard() {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (Hexxagon.isValid(i, j)) {
                    board[i][j] = new Piece(i, j, Piece.EMPTY);
                }
            }
        }
        board[0][0].setTo(Piece.COMPUTER);
        board[8][4].setTo(Piece.COMPUTER);
        board[4][8].setTo(Piece.COMPUTER);


        board[8][8].setTo(Piece.HUMAN);
        board[0][4].setTo(Piece.HUMAN);
        board[4][0].setTo(Piece.HUMAN);


        countPieces[Piece.COMPUTER] = 3;
        countPieces[Piece.HUMAN] = 3;

        currentPlayer = Piece.COMPUTER;
    }

    public GameBoard(Object obj) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (Hexxagon.isValid(i, j)) {
                    board[i][j] = new Piece(i, j);
                }
            }
        }
    }

    /**
     * copy this board
     * @return
     */
    public GameBoard copyBoard() {
        GameBoard boardCopy = new GameBoard(null);

        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (Hexxagon.isValid(i, j)) {
                    boardCopy.getBoard()[i][j].setStatus(this.board[i][j].getStatus());
                }
            }
        }

        boardCopy.currentPlayer = this.currentPlayer;
        boardCopy.countPieces[Piece.COMPUTER] = this.countPieces[Piece.COMPUTER];
        boardCopy.countPieces[Piece.HUMAN] = this.countPieces[Piece.HUMAN];

        return boardCopy;
    }

    /**
     * mảng lưu trữ danh sach các ô cờ hợp lệ mà cách Piece pFrom 1 ô cờ
     * @param Piece pFrom
     * @return ArrayList<Piece>
     */
    public ArrayList<Piece> doubling(Piece pFrom) {
        ArrayList<Piece> listP = new ArrayList<Piece>();

        int x = pFrom.getX();
        int y = pFrom.getY();

        Piece p1 = Hexxagon.isValid(x - 1, y) ? board[x - 1][y] : null;
        if (p1 != null && p1.getStatus() == Piece.EMPTY) {
            listP.add(p1);
        }

        Piece p2 = Hexxagon.isValid(x, y - 1) ? board[x][y - 1] : null;
        if (p2 != null && p2.getStatus() == Piece.EMPTY) {
            listP.add(p2);
        }

        Piece p3 = Hexxagon.isValid(x + 1, y) ? board[x + 1][y] : null;
        if (p3 != null && p3.getStatus() == Piece.EMPTY) {
            listP.add(p3);
        }

        Piece p4 = Hexxagon.isValid(x, y + 1) ? board[x][y + 1] : null;
        if (p4 != null && p4.getStatus() == Piece.EMPTY) {
            listP.add(p4);
        }

        Piece p5 = Hexxagon.isValid(x - 1, y - 1) ? board[x - 1][y - 1] : null;
        if (p5 != null && p5.getStatus() == Piece.EMPTY) {
            listP.add(p5);
        }

        Piece p6 = Hexxagon.isValid(x + 1, y + 1) ? board[x + 1][y + 1] : null;
        if (p6 != null && p6.getStatus() == Piece.EMPTY) {
            listP.add(p6);
        }

        return listP;
    }

    /**
     * mảng lưu trữ danh sách các ô cờ hợp lệ mà cách Piece pFrom 2 ô cờ
     * @param Piece pFrom
     * @return ArrayList<Piece> mang luu tru cac o co
     */
    public ArrayList<Piece> jumping(Piece pFrom) {
        ArrayList<Piece> listP = new ArrayList<Piece>();

        int x = pFrom.getX();
        int y = pFrom.getY();

        Piece p1 = Hexxagon.isValid(x - 2, y) ? board[x - 2][y] : null;
        if (p1 != null && p1.getStatus() == Piece.EMPTY) {
            listP.add(p1);
        }

        Piece p2 = Hexxagon.isValid(x - 2, y - 1) ? board[x - 2][y - 1] : null;
        if (p2 != null && p2.getStatus() == Piece.EMPTY) {
            listP.add(p2);
        }

        Piece p3 = Hexxagon.isValid(x - 2, y - 2) ? board[x - 2][y - 2] : null;
        if (p3 != null && p3.getStatus() == Piece.EMPTY) {
            listP.add(p3);
        }

        Piece p4 = Hexxagon.isValid(x - 1, y - 2) ? board[x - 1][y - 2] : null;
        if (p4 != null && p4.getStatus() == Piece.EMPTY) {
            listP.add(p4);
        }

        Piece p5 = Hexxagon.isValid(x, y - 2) ? board[x][y - 2] : null;
        if (p5 != null && p5.getStatus() == Piece.EMPTY) {
            listP.add(p5);
        }

        Piece p6 = Hexxagon.isValid(x + 1, y - 1) ? board[x + 1][y - 1] : null;
        if (p6 != null && p6.getStatus() == Piece.EMPTY) {
            listP.add(p6);
        }

        Piece p7 = Hexxagon.isValid(x + 2, y) ? board[x + 2][y] : null;
        if (p7 != null && p7.getStatus() == Piece.EMPTY) {
            listP.add(p7);
        }

        Piece p8 = Hexxagon.isValid(x + 2, y + 1) ? board[x + 2][y + 1] : null;
        if (p8 != null && p8.getStatus() == Piece.EMPTY) {
            listP.add(p8);
        }

        Piece p9 = Hexxagon.isValid(x + 2, y + 2) ? board[x + 2][y + 2] : null;
        if (p9 != null && p9.getStatus() == Piece.EMPTY) {
            listP.add(p9);
        }

        Piece p10 = Hexxagon.isValid(x + 1, y + 2) ? board[x + 1][y + 2] : null;
        if (p10 != null && p10.getStatus() == Piece.EMPTY) {
            listP.add(p10);
        }

        Piece p11 = Hexxagon.isValid(x, y + 2) ? board[x][y + 2] : null;
        if (p11 != null && p11.getStatus() == Piece.EMPTY) {
            listP.add(p11);
        }

        Piece p12 = Hexxagon.isValid(x - 1, y + 1) ? board[x - 1][y + 1] : null;
        if (p12 != null && p12.getStatus() == Piece.EMPTY) {
            listP.add(p12);
        }

        return listP;
    }

    /*
     * kiem tra mot nuoc di tu Piece from toi Piece to co cach 1 o hay khong
     * @param Piece from, Piece to
     * @return true neu to cach from 1 o; false neu cach 2 o
     */
    public boolean isDoubling(Piece from, Piece to) {
        int xF = from.getX();
        int yF = from.getY();
        int xT = to.getX();
        int yT = to.getY();
        boolean r = (xF == xT - 1 && yF == yT) || (xF == xT && yF == yT - 1)
                || (xF == xT + 1 && yF == yT) || (xF == xT && yF == yT + 1)
                || (xF == xT - 1 && yF == yT - 1) || (xF == xT + 1 && yF == yT + 1);
        return r;
    }

    /*
     * kiem tra 1 nuoc di co la nhay hay ko
     * @param Piece from : o co ma tu do di chuyen di
     * @param Piece to : o co ma tu do from duoc chuyen toi
     * @return true neu from cach to 2 o, va false neu cach 1 o
     */
    public boolean isJumping(Piece from, Piece to) {
        int xF = from.getX();
        int yF = from.getY();
        int xT = to.getX();
        int yT = to.getY();
        boolean r = (xF == xT - 2 && yF == yT - 2) || (xF == xT + 2 && yF == yT + 2)
                || (xF == xT - 2 && yF == yT - 1) || (xF == xT + 2 && yF == yT + 1)
                || (xF == xT - 2 && yF == yT) || (xF == xT + 2 && yF == yT)
                || (xF == xT - 1 && yF == yT + 1) || (xF == xT + 1 && yF == yT - 1)
                || (xF == xT && yF == yT + 2) || (xF == xT && yF == yT - 2)
                || (xF == xT + 1 && yF == yT + 2) || (xF == xT - 1 && yF == yT - 2);
        return r;
    }

    /**
     * Kiểm tra xem nước đi này có hợp lệ
     * @param pFrom ô cờ mà từ đây được di chuyển
     * @param pTo ô cờ mà quân cờ pForm được chuyển đến
     * @return <code>true</code> nếu nước đi hợp lệ, <code>false</code> nếu ngược lại
     */
    public boolean isValidMove(Piece pFrom, Piece pTo) {
        ArrayList<Piece> list1 = doubling(pFrom);
        ArrayList<Piece> list2 = jumping(pFrom);

        for (int i = 0; i < list1.size(); i++) {
            if (pTo == list1.get(i)) {
                return true;
            }
        }

        for (int j = 0; j < list2.size(); j++) {
            if (pTo == list2.get(j)) {
                return true;
            }
        }

        return false;
    }

    /*
     * kiem tra trang thai ket thuc cua ban co
     * @param
     * @return true neu tong so quan cua hai ben = 58
     *         false neu 1 trong hai ben khong con nuoc di
     */
    public boolean isEndGame() {
        if (countPieces[Piece.COMPUTER] + countPieces[Piece.HUMAN] == 58) {
            return true;
        }

        if (getAllValidMoves(currentPlayer).size() == 0) {
            this.countPieces[3 - currentPlayer] = 58 - this.countPieces[currentPlayer];
            for (int i = 0; i < 9; i++) {
                for (int j = 0; j < 9; j++) {
                    if (Hexxagon.isValid(i, j) && board[i][j].getStatus() == Piece.EMPTY) {
                        board[i][j].setGraphic("piece_" + (3 - currentPlayer));
                    }
                }
            }
            return true;
        }

        return false;
    }

    /*
     * di chuyen tu Piece pFrom toi Piece pTo, cap nhat so quan duoc an va trang thai ban co
     * @param Piece pFrom
     * @param Piece pTo
     * @return this board: trang thai moi cua ban co sau khi thuc hien nuoc di tu pFrom toi pTo
     */
    public GameBoard move(Piece pFrom, Piece pTo) {
        if (isDoubling(pFrom, pTo)) { // neu di chuyen den pTo phai nhan doi quan co
            this.board[pTo.getX()][pTo.getY()].setStatus(pFrom.getStatus());
            if (this.board[pTo.getX()][pTo.getY()].getGraphic() != null) {
                this.board[pTo.getX()][pTo.getY()].setGraphic("piece_" + pFrom.getStatus());
            }
            countPieces[currentPlayer] += 1;
        } else if (isJumping(pFrom, pTo)) { // neu di chuyen den pTo phai nhay quan co
            if (this.board[pTo.getX()][pTo.getY()].getGraphic() != null) {
                this.board[pTo.getX()][pTo.getY()].setGraphic("piece_" + pFrom.getStatus());
                this.board[pFrom.getX()][pFrom.getY()].setGraphic("piece_" + Piece.EMPTY);
            }
            this.board[pTo.getX()][pTo.getY()].setStatus(pFrom.getStatus());
            this.board[pFrom.getX()][pFrom.getY()].setStatus(Piece.EMPTY);
        }

        ArrayList<Piece> listPiecesIsAte = this.eatPiecesOfOpponent(this.board[pTo.getX()][pTo.getY()]);

        Piece p = null;

        for (int i = 0; i < listPiecesIsAte.size(); i++) {
            p = listPiecesIsAte.get(i);
            this.board[p.getX()][p.getY()].setStatus(this.board[pTo.getX()][pTo.getY()].getStatus());
            if (p.getGraphic() != null) {
                this.board[p.getX()][p.getY()].setGraphic("piece_" + this.board[pTo.getX()][pTo.getY()].getStatus());
            }
        }

        countPieces[currentPlayer] += listPiecesIsAte.size();
        countPieces[3 - currentPlayer] -= listPiecesIsAte.size();

        return this;
    }

    /**
     * An quan cua doi thu: an tat ca cac quan dung canh p nhung ko cung loai voi p
     * @param Piece p
     * @return Arraylist<Piece> chua cac quan se bi an
     */
    public ArrayList<Piece> eatPiecesOfOpponent(Piece p) {
        ArrayList<Piece> listPiecesIsAte = new ArrayList<Piece>();

        int x = p.getX();
        int y = p.getY();
        int s = p.getStatus();

        Piece p1 = Hexxagon.isValid(x - 1, y) ? this.board[x - 1][y] : null;
        if ((p1 != null) && (p1.getStatus() == 3 - s)) {
            listPiecesIsAte.add(p1);
        }

        Piece p2 = Hexxagon.isValid(x, y - 1) ? this.board[x][y - 1] : null;
        if ((p2 != null) && (p2.getStatus() == 3 - s)) {
            listPiecesIsAte.add(p2);
        }

        Piece p3 = Hexxagon.isValid(x + 1, y) ? this.board[x + 1][y] : null;
        if ((p3 != null) && (p3.getStatus() == 3 - s)) {
            listPiecesIsAte.add(p3);
        }

        Piece p4 = Hexxagon.isValid(x, y + 1) ? this.board[x][y + 1] : null;
        if ((p4 != null) && (p4.getStatus() == 3 - s)) {
            listPiecesIsAte.add(p4);
        }

        Piece p5 = Hexxagon.isValid(x - 1, y - 1) ? this.board[x - 1][y - 1] : null;
        if ((p5 != null) && (p5.getStatus() == 3 - s)) {
            listPiecesIsAte.add(p5);
        }

        Piece p6 = Hexxagon.isValid(x + 1, y + 1) ? this.board[x + 1][y + 1] : null;
        if ((p6 != null) && (p6.getStatus() == 3 - s)) {
            listPiecesIsAte.add(p6);
        }

        return listPiecesIsAte;
    }
    Move bMove = null;

    public Move getBestMove() {
        return bMove;
    }
    public static int prunned = 0;
    public static int call = 0;

    /*
     * ham tinh gia tri cua ban co hien tai
     * @param GameBoard currentBoard: trang thai hien tai cua ban co
     * @param int depth: do sau gioi han tren cay tim kiem
     * @param int alpha: gia tri cua nuoc di tot nhat doi voi may
     * @param int beta: gia tri cua nuoc di tot nhat doi voi nguoi
     * @param boolean returnBestMove: kiem tra da tra ve nuoc di tot nhat chua
     * @return int: gia tri ung voi nuoc tot nhat cua nguoi choi hien tai
     */
    public int alphaBetaPrunning(GameBoard currentBoard, int depth, int alpha, int beta, boolean returnBestMove) {

        if (currentBoard.isEndGame() || depth == 0) {
            return evaluate(currentBoard);
        }

        ArrayList<Move> validMoves = currentBoard.getAllValidMoves(currentBoard.getCurrentPlayer());

        if (returnBestMove) {
            prunned = 0;
            call = 0;
            this.bMove = null;
            if (validMoves.size() > 0) {
                this.bMove = validMoves.get(0);
            }
        }

        if (call >= 1000000){
            return evaluate(currentBoard);
        }

        if (currentBoard.getCurrentPlayer() == Piece.COMPUTER) {
            for (int i = 0; i < validMoves.size(); i++) {
                GameBoard copy = currentBoard.copyBoard();
                Move move = validMoves.get(i);
                copy.move(move.getPFrom(), move.getPTo());
                copy.setCurrentPlayer(Piece.HUMAN);
                call++;

                int score = alphaBetaPrunning(copy, depth - 1, alpha, beta, false);

                if (score > alpha) {
                    alpha = score;
                    if (returnBestMove) {
                        this.bMove = move;
                    }
                }
                if (alpha >= beta) {
                    prunned++;
                    return alpha;
                }
                try {
                    copy.finalize();
                } catch (Throwable ex) {
                    Logger.getLogger(GameBoard.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            return alpha;
        } else {
            for (int i = 0; i < validMoves.size(); i++) {
                GameBoard copy = currentBoard.copyBoard();
                Move move = validMoves.get(i);
                copy.move(move.getPFrom(), move.getPTo());
                copy.setCurrentPlayer(Piece.COMPUTER);
                call++;

                int score = alphaBetaPrunning(copy, depth - 1, alpha, beta, false);
                copy.setCurrentPlayer(Piece.HUMAN);
                if (score < beta) {
                    beta = score;
                }
                if (alpha >= beta) {
                    prunned++;
                    return beta;
                }
                try {
                    copy.finalize();
                } catch (Throwable ex) {
                    Logger.getLogger(GameBoard.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            return beta;
        }
    }

    /*
     * ham uoc luong gia tri cua 1 ban co
     * @param GameBoard board
     * @return int : gia tri duoc tinh bang so quan cua may nhan 10 tru so quan cua nguoi
     */
    public int evaluate(GameBoard board) {
        return board.countPieces[Piece.COMPUTER] - board.countPieces[Piece.HUMAN] ;
//                / Math.abs(board.countPieces[Piece.COMPUTER] - board.countPieces[Piece.HUMAN])
//                - board.countPieces[Piece.HUMAN];
    }

    /*
     * lay ra tat ca cac nuoc di hop le cua 1 ben
     * @param int player : nguoi choi hien tai
     * @return ArrayList<Move> : mang cac nuoc di hop le
     */
    public ArrayList<Move> getAllValidMoves(int player) {
        ArrayList<Piece> listPiecesOfPlayer = getPiecesOfPlayer(player);

        ArrayList<Move> moves = new ArrayList<Move>();

        for (int i = 0; i < listPiecesOfPlayer.size(); i++) {
            ArrayList<Piece> list1 = doubling(listPiecesOfPlayer.get(i));
            ArrayList<Piece> list2 = jumping(listPiecesOfPlayer.get(i));

            for (int j = 0; j < list1.size(); j++) {
                moves.add(new Move(listPiecesOfPlayer.get(i), list1.get(j)));
            }
            for (int j = 0; j < list2.size(); j++) {
                moves.add(new Move(listPiecesOfPlayer.get(i), list2.get(j)));
            }
            /////////
//            for (int j = 0; j < list1.size(); j++) {
//                int countEatable = eatPiecesOfOpponent(list1.get(j)).size();
//                if (countEatable == 0){
//                    // add vào đuôi
//                    moves.add(new Move(listPiecesOfPlayer.get(i), list1.get(j)));
//                }
//                else if (countEatable == 1){
//                    moves.add(equal1, new Move(listPiecesOfPlayer.get(i), list1.get(j)));
//                }
//                else if (countEatable == 2){
//                    moves.add(equal2, new Move(listPiecesOfPlayer.get(i), list1.get(j)));
//                    equal1++;
//                }
//                else if (countEatable == 3){
//                    moves.add(equal3, new Move(listPiecesOfPlayer.get(i), list1.get(j)));
//                    equal1++;
//                    equal2++;
//                }
//                else if (countEatable > 3){
//                    // add vào đầu
//                    moves.add(0, new Move(listPiecesOfPlayer.get(i), list1.get(j)));
//                    equal1++;
//                    equal2++;
//                    equal3++;
//                }
//            }
//
//            ///////////////
//            for (int j = 0; j < list2.size(); j++) {
//                int countEatable = eatPiecesOfOpponent(list2.get(j)).size();
//                if (countEatable == 0){
//                    // add vào đuôi
//                    moves.add(new Move(listPiecesOfPlayer.get(i), list2.get(j)));
//                }
//                else if (countEatable == 1){
//                    moves.add(equal1, new Move(listPiecesOfPlayer.get(i), list2.get(j)));
//                }
//                else if (countEatable == 2){
//                    moves.add(equal2, new Move(listPiecesOfPlayer.get(i), list2.get(j)));
//                    equal1++;
//                }
//                else if (countEatable == 3){
//                    moves.add(equal3, new Move(listPiecesOfPlayer.get(i), list2.get(j)));
//                    equal1++;
//                    equal2++;
//                }
//                else if (countEatable > 3){
//                    // add vào đầu
//                    moves.add(0, new Move(listPiecesOfPlayer.get(i), list2.get(j)));
//                    equal1++;
//                    equal2++;
//                    equal3++;
//                }
//            }
            
        }

        return moves;
    }

    /*
     * lay ra tat ca cac quan tren ban co cua 1 ben nao do
     * @param int player : nguoi choi hien tai
     * @return ArrayList<Piece> mang chua cac quan co duoc lay ra
     */
    public ArrayList<Piece> getPiecesOfPlayer(int player) {
        ArrayList<Piece> listPieceOfPlayer = new ArrayList<Piece>();

        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (Hexxagon.isValid(i, j) && this.board[i][j].getStatus() == player) {
                    listPieceOfPlayer.add(board[i][j]);
                }
            }
        }

        return listPieceOfPlayer;
    }
}
